using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using ShootThemUp.Sources.GameElements.Ships;
using ShootThemUp.Sources.GameElements.Ships.Player;
using ShootThemUp.Sources.GameElements.Ships.Ennemies.Fruits;

namespace ShootThemUp.Sources.GameElements.Weapons
{
    /// <summary>
    /// This class instanciate shots in function of some parameters(type, number of shot...).
    /// </summary>
    public static class ShotFactory
    {
        #region Simple Shot Constant Fields

        /// <summary>
        /// The X position of the simple shot texture in the spritesheet (in px).
        /// </summary>
        private const int SIMPLE_SHOT_POSITION_X = 0;
        /// <summary>
        /// The Y position of the simple shot texture in the spritesheet (in px).
        /// </summary>
        private const int SIMPLE_SHOT_POSITION_Y = 85;
        /// <summary>
        /// The width of the simple shot texture in the spritesheet (in px).
        /// </summary>
        private const int SIMPLE_SHOT_WIDTH = 5;
        /// <summary>
        /// The height of the simple shot texture in the spritesheet (in px).
        /// </summary>
        private const int SIMPLE_SHOT_HEIGHT = 23;
        /// <summary>
        /// The damages of a simple shot.
        /// </summary>
        private const int SIMPLE_SHOT_DAMAGES = 230;
        /// <summary>
        /// The speed of a simple shot, in pixel per ms.
        /// </summary>
        private const float SIMPLE_SHOT_SPEED = 0.8f;
        /// <summary>
        /// The malus for the speed of ennemies shots 
        /// </summary>
        private const int SIMPLE_SHOT_ENNEMIES_SPEED_MALUS = 5;
        /// <summary>
        /// The malus for ennemies shots damages.
        /// </summary>
        private const int SIMPLE_SHOT_ENNEMIES_DAMAGES_MALUS = 2;
        /// <summary>
        /// The life time of a simple shot, in ms.
        /// </summary>
        private const float SIMPLE_SHOT_LIFE_TIME = 4000;
        /// <summary>
        /// The gap between two parallal simple shots.
        /// </summary>
        private const int SIMPLE_SHOT_GAP = 6;

        #endregion
        
        #region Ball Shot Constant Fields

        /// <summary>
        /// The X position of the ball shot texture in the spritesheet (in px).
        /// </summary>
        private const int BALL_SHOT_POSITION_X = 5;
        /// <summary>
        /// The Y position of the ball shot texture in the spritesheet (in px).
        /// </summary>
        private const int BALL_SHOT_POSITION_Y = 85;
        /// <summary>
        /// The width of the ball shot texture in the spritesheet (in px).
        /// </summary>
        private const int BALL_SHOT_WIDTH = 11;
        /// <summary>
        /// The height of the ball shot texture in the spritesheet (in px).
        /// </summary>
        private const int BALL_SHOT_HEIGHT = 11;
        /// <summary>
        /// The damages of a ball shot.
        /// </summary>
        private const int BALL_SHOT_DAMAGES = 180;
        /// <summary>
        /// The speed of a simple shot, in pixel per ms.
        /// </summary>
        private const float BALL_SHOT_SPEED = 0.5f;
        /// <summary>
        /// The malus for the speed of ennemies shots 
        /// </summary>
        private const int BALL_SHOT_ENNEMIES_SPEED_MALUS = 3;
        /// <summary>
        /// The malus for ennemies shots damages.
        /// </summary>
        private const int BALL_SHOT_ENNEMIES_DAMAGES_MALUS = 2;
        /// <summary>
        /// The life time of a simple shot, in ms.
        /// </summary>
        private const float BALL_SHOT_LIFE_TIME = 2000;
        /// <summary>
        /// The angle between two ball shot. In radians.
        /// </summary>
        private const float BALL_SHOT_ANGLE = (float)Math.PI / 75;
        /// <summary>
        /// the margin between the ships and the shot
        /// </summary>
        private const int MARGIN = 10;

        #endregion

        #region Factory Methods

        /// <summary>
        /// Creates the shots in function of the weapon of the ship given and at the position of the ship given.
        /// </summary>
        /// <param name="ship">The ship which fire.</param>
        /// <returns>The shots created.</returns>
        public static List<Shot> CreateShots(Ship ship)
        {
            List<Shot> shots = null;

            switch (ship.Weapon.WeaponType)
            {
                case WeaponType.MultiBallShot: shots = CreateBallShots(ship); break;
                case WeaponType.MultiSimpleShot: shots = CreateSimpleShots(ship); break;
            }

            if(ship is PlayerShip)
            {
                for (int i = 0; i < shots.Count; i++)
                {
                    if (i % 2 == 0)
                        shots[i].Color = Color.LightCoral;
                    else
                        shots[i].Color = Color.Cyan;
                }
            }

            return shots;
        }

        /// <summary>
        /// Creates simple shots.
        /// </summary>
        /// <param name="ship">The ship which fire.</param>
        /// <returns></returns>
        private static List<Shot> CreateSimpleShots(Ship ship)
        {            
            List<Shot> shots = new List<Shot>(ship.Weapon.NumberOfShot);
            int gap = (ship.SourceRectangle.Value.Width - ShotFactory.SIMPLE_SHOT_GAP) / ship.Weapon.NumberOfShot;
            int damages = (ship is PlayerShip) ? ShotFactory.SIMPLE_SHOT_DAMAGES : ShotFactory.SIMPLE_SHOT_DAMAGES / ShotFactory.SIMPLE_SHOT_ENNEMIES_DAMAGES_MALUS;

            //Shot Texture
            Rectangle shotTexture = (ship.Weapon.ShotTexture == null) ? new Rectangle(SIMPLE_SHOT_POSITION_X, SIMPLE_SHOT_POSITION_Y, SIMPLE_SHOT_WIDTH, SIMPLE_SHOT_HEIGHT) : (Rectangle)ship.Weapon.ShotTexture;
            
            for (int i = 0; i < ship.Weapon.NumberOfShot; i++)
            {
                float x = ShotFactory.SIMPLE_SHOT_GAP + ship.Position.X + i * gap;
                float y = (ship is PlayerShip) ? ship.Position.Y + ShotFactory.MARGIN : ship.Position.Y + ship.SourceRectangle.Value.Height - ShotFactory.MARGIN;

                float speedY = - ShotFactory.SIMPLE_SHOT_SPEED;

                if (ship is Fruit)
                    speedY /= - ShotFactory.SIMPLE_SHOT_ENNEMIES_SPEED_MALUS;

                shots.Add(new Shot(damages, ShotFactory.SIMPLE_SHOT_LIFE_TIME, !(ship is PlayerShip), new Vector2(x, y), new Vector2(0, speedY), shotTexture));
            }

            return shots;
        }

        /// <summary>
        /// Creates ball shots.
        /// </summary>
        /// <param name="numberOfShot">The number of shot.</param>
        /// <param name="position">The position of the ship which fire. Middle of the ship in x, top of the ship in y.</param>
        /// <returns></returns>
        private static List<Shot> CreateBallShots(Ship ship)
        {
            // Number of shot, at the left and at the right of the center shot.
            int annexeShot = ship.Weapon.NumberOfShot / 2;

            int damages = (ship is PlayerShip) ? ShotFactory.BALL_SHOT_DAMAGES : ShotFactory.BALL_SHOT_DAMAGES / ShotFactory.BALL_SHOT_ENNEMIES_DAMAGES_MALUS;

            // Init Position of the shots, the middle top position of the ship
            Vector2 position = new Vector2(ship.Position.X + ship.SourceRectangle.Value.Width / 2 - ShotFactory.BALL_SHOT_WIDTH / 2, ship.Position.Y);

            float speedY = -ShotFactory.SIMPLE_SHOT_SPEED;

            if (ship is Fruit)
                speedY /= -ShotFactory.SIMPLE_SHOT_ENNEMIES_SPEED_MALUS;

            //Returned list.
            List<Shot> shots = new List<Shot>(ship.Weapon.NumberOfShot);

            //Shot Texture
            Rectangle shotTexture = (ship.Weapon.ShotTexture == null) ? new Rectangle(BALL_SHOT_POSITION_X, BALL_SHOT_POSITION_Y, BALL_SHOT_WIDTH, BALL_SHOT_HEIGHT) : (Rectangle)ship.Weapon.ShotTexture;

            // Central shot, speedX = 0
            shots.Add(new Shot(damages, ShotFactory.BALL_SHOT_LIFE_TIME, !(ship is PlayerShip), position, new Vector2(0, speedY), shotTexture));

            // Right shot(s), speedX > 0
            for (int i = 0; i < annexeShot; i++)
            {
                float annexeSpeedX = - speedY * (float)Math.Cos((i + 1) * BALL_SHOT_ANGLE - Math.PI / 2);
                float annexeSpeedY = - speedY * (float)Math.Sin((i + 1) * BALL_SHOT_ANGLE - Math.PI / 2);

                Vector2 speed = new Vector2(annexeSpeedX, annexeSpeedY);

                shots.Add(new Shot(damages, ShotFactory.BALL_SHOT_LIFE_TIME, !(ship is PlayerShip), position, speed, shotTexture));
            }

            // Left shot(s), speedX < 0
            for (int i = 0; i < annexeShot; i++)
            {
                float annexeSpeedX = - speedY * (float)Math.Cos(-(i + 1) * BALL_SHOT_ANGLE - Math.PI / 2);
                float annexeSpeedY = - speedY * (float)Math.Sin(-(i + 1) * BALL_SHOT_ANGLE - Math.PI / 2);

                Vector2 speed = new Vector2(annexeSpeedX, annexeSpeedY);

                shots.Add(new Shot(damages, ShotFactory.BALL_SHOT_LIFE_TIME, !(ship is PlayerShip), position, speed, shotTexture));
            }

            return shots;
        }

        #endregion
    }
}
